module share.dlist;


struct DList(T)
{
	private struct Node
	{
		T		ptypeData;
		Node*	pNextNode;
		Node*	pPrevNode;
			
		this(ref T pData, Node* pNext = null, Node* pPrev = null)
		{
			ptypeData	= pData;
			pNextNode	= pNext;
			pPrevNode	= pPrev;
		}
	}
		
	alias Node* PNode;

	private PNode m_stHead = null;
	private PNode m_pstCurrNode = null;
	private PNode m_stTail = null;
	private int m_nCurrentPos = 0;
	private int m_nCounter = 0;


	
	/*public this(DList!T list)
	{
		for ( int nLoops = 0; nLoops < list.ListLength(); nLoops++ )
		{
			T* tempT = new T;
			tempt.ptypeData = list.Current();
			AddNode(tempT);
		}
	}*/

	public ~this()
	{
		ClearAllNodes(false);
	}

	public void AddNode(ref T pData)
	{
        PNode pstTempNode = new Node(pData);
		if ( m_stTail !is null )
		{
			m_stTail.pNextNode	  = pstTempNode;
			pstTempNode.pNextNode = pstTempNode;
			pstTempNode.pPrevNode = m_stTail;
			m_stTail = m_stTail.pNextNode;
		}
		else
		{
			m_stHead				= pstTempNode;
			m_stHead.pPrevNode		= m_stHead;
			m_stHead.pNextNode		= m_stHead;
			m_stTail				= m_stHead;
			m_pstCurrNode			= m_stHead;
		}
		m_nCounter++;
	}

	public bool DeleteCurrentNode()
	{
		PNode pstTempNode;
		
		if ( m_pstCurrNode !is null )
		{
			m_nCounter--;
			m_pstCurrNode.pPrevNode.pNextNode = m_pstCurrNode.pNextNode;
			m_pstCurrNode.pNextNode.pPrevNode = m_pstCurrNode.pPrevNode;

			pstTempNode		= m_pstCurrNode;
			m_pstCurrNode	= m_pstCurrNode.pNextNode;
            if ( pstTempNode is m_pstCurrNode )				// Current = Tail ?
			{
				m_stTail = m_stTail.pPrevNode;
				m_pstCurrNode = m_pstCurrNode.pPrevNode;
				m_pstCurrNode.pNextNode = m_pstCurrNode;
				m_nCurrentPos--;
                if ( m_pstCurrNode is pstTempNode )			// Current = Head = Tail ?
				{
					m_stHead = null;
					m_stTail = null;
					m_pstCurrNode = null;
					m_nCurrentPos = 0;
				}
			}
			else if (pstTempNode is m_stHead)				// Current  = Head ?
			{
				m_stHead = pstTempNode.pNextNode;
				m_pstCurrNode = pstTempNode.pNextNode;
				m_pstCurrNode.pPrevNode = m_pstCurrNode;
				if ( pstTempNode is m_stHead )			// Current = Head = Tail ?
				{
					m_stHead = null;
					m_stTail = null;
					m_pstCurrNode = null;
					m_nCurrentPos = 0;
				}
			}
			pstTempNode = null;
			return true;
		}
		
		return false;
	}

	// ¿ÜºÎ¿¡¼­ typeData¸¦ Áö¿ì´Â ÇüÅÂÀÌ´Ù.
	public bool DeleteCurrentNodeEx()
	{
		PNode pstTempNode;
		
		if ( m_pstCurrNode !is null )
		{
			m_nCounter--;
			m_pstCurrNode.pPrevNode.pNextNode = m_pstCurrNode.pNextNode;
			m_pstCurrNode.pNextNode.pPrevNode = m_pstCurrNode.pPrevNode;

			pstTempNode		= m_pstCurrNode;
			m_pstCurrNode	= m_pstCurrNode.pNextNode;
            if ( pstTempNode is m_pstCurrNode )				// Current = Tail ?
			{
				m_stTail = m_stTail.pPrevNode;
				m_pstCurrNode = m_pstCurrNode.pPrevNode;
				m_pstCurrNode.pNextNode = m_pstCurrNode;
				m_nCurrentPos--;
                if ( m_pstCurrNode is pstTempNode )			// Current = Head = Tail ?
				{
					m_stHead = null;
					m_stTail = null;
					m_pstCurrNode = null;
					m_nCurrentPos = 0;
				}
			}
            else if (pstTempNode is m_stHead)				// Current  = Head ?
			{
				m_stHead = pstTempNode.pNextNode;
				m_pstCurrNode = pstTempNode.pNextNode;
				m_pstCurrNode.pPrevNode = m_pstCurrNode;
                if ( pstTempNode is m_stHead )			// Current = Head = Tail ?
				{
					m_stHead = null;
					m_stTail = null;
					m_pstCurrNode = null;
					m_nCurrentPos = 0;
				}
			}
            destroy(pstTempNode.ptypeData);
            pstTempNode = null;
			return true;
		}
		
		return false;
	}
	
	public bool RemoveNodeByData(in T pData)
	{
		PNode pstTempNode, pNode = null;
		
		if ( m_nCounter > 0 )
		{
			for ( int nLoops = 0; nLoops < m_nCounter; nLoops++ )
			{
				pstTempNode = m_stHead;
				m_stHead	= m_stHead.pNextNode;
				
				if (pData is pstTempNode.ptypeData)
				{
					pNode = pstTempNode;
					break;
				}
			}
		}
		
		if ( pNode !is null )
		{
			m_pstCurrNode = pNode;
			return DeleteCurrentNode();
		}
		
		return false;
	}

    public int FindNode(in T pData)
    {
        PNode pstTempNode;
        int result = -1;
        
        if ( m_nCounter > 0 )
        {
            for ( int nLoops = 0; nLoops < m_nCounter; nLoops++ )
            {
                pstTempNode = m_stHead;
                m_stHead    = m_stHead.pNextNode;
                
                if (pData is pstTempNode.ptypeData)
                {
                    result = nLoops;
                    break;
                }
            }
        }
        
        return result;
    }

	public int ListLength()
	{
		return m_nCounter;
	}
	
	public int MoveNextNode()
	{
		PNode pstTempNode;

		if ( (m_pstCurrNode !is null) && (m_pstCurrNode.pNextNode !is null) )
		{
			pstTempNode		= m_pstCurrNode;
			m_pstCurrNode	= pstTempNode.pNextNode;
			m_nCurrentPos	= (m_nCurrentPos<m_nCounter) ? m_nCurrentPos+1: m_nCounter;
		}

		return m_nCurrentPos;
	}

	public int MovePreviousNode()
	{
		PNode pstTempNode;
		pstTempNode		= m_pstCurrNode;
		m_pstCurrNode	= pstTempNode.pPrevNode;
		m_nCurrentPos	= (m_nCurrentPos>1) ? m_nCurrentPos-1: 1;

		return m_nCurrentPos;
	}

	public int MoveNode(int nMovement)
	{
		if ( nMovement > 0 )
		{
			for ( int nLoops = 0; nLoops < nMovement; nLoops++ )
				MoveNextNode();
		}
		else
		{
			nMovement = (-nMovement);
			for ( int nLoops = 0; nLoops < nMovement; nLoops++)
				MovePreviousNode();
		}
		return m_nCurrentPos;
	}

	public void MoveCurrentToTop()
	{
		m_pstCurrNode = m_stHead;	
		m_nCurrentPos = 0;
	}

	public void MoveCurrentToTail()
	{
		m_pstCurrNode = m_stTail;	
		m_nCurrentPos = m_nCounter-1;
	}

	public void ClearAllNodes(bool deleteData = false)
	{
		PNode pstTempNode;
		if ( m_nCounter > 0 )
		{
			for ( int nLoops = 0; nLoops < m_nCounter; nLoops++ )
			{
				pstTempNode = m_stHead;
				m_stHead	= m_stHead.pNextNode;
				if (deleteData)
                    destroy(pstTempNode.ptypeData);
                pstTempNode = null;
			}
		}
		m_nCounter		= 0;
		m_stHead		= null;
		m_stTail		= null;
		m_pstCurrNode	= null;
		m_nCurrentPos	= 0;
	}


    // INFO: foreach
    public int opApply (int delegate(ref int index, ref T value) dg)
    {
        PNode pstTempNode;
        int result = 0;

        if ( m_nCounter > 0 )
        {
            for ( int nLoops = 0; nLoops < m_nCounter; nLoops++ )
            {
                pstTempNode = m_stHead;
                m_stHead    = m_stHead.pNextNode;

                result = dg (nLoops, pstTempNode.ptypeData);
                if (result != 0) break;
            }
        }

        return result;
    }

	public ref T getCurrentData()
	{
		return m_pstCurrNode.ptypeData;
	}

	public int getCounter()
	{
		return m_nCounter;
	}

	public int getCurrPosition()
	{
		return m_nCurrentPos;
	}

	public bool isCurrentTail()
	{
        if ( m_pstCurrNode is m_stTail )
			return true;
		else
			return false;
	}

	public bool isCurrentHead()
	{
        if (m_pstCurrNode is m_stHead )
			return true;
		else
			return false;
	}

	public bool isEmpty()
	{
		if ( m_stHead is null )
			return true;
		else
			return false;
	}
}
